use anyhow::{anyhow, Result};
use reflectionDemo::{reflect_method, ReflectCall};
use serde::{Deserialize, Serialize};
use serde_json::{Value as JsonValue, Value};
use std::any::Any;
use std::future::Future;
use std::pin::Pin;
use std::collections::HashMap;

pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;

pub struct MethodRegistration {
    pub struct_name: &'static str,
    pub method: ReflectedMethod,
}

inventory::collect!(MethodRegistration);

// 反射方法表示
#[derive(Clone)]
pub struct ReflectedMethod {
    pub name: &'static str,
    pub param_count: usize,
    pub is_async: bool,
    // pub is_mut: bool,
    pub function: fn(&mut dyn ReflectCall, JsonValue) -> BoxFuture<'_, Result<JsonValue>>,
}

impl ReflectedMethod {
    /// 调用反射方法
    pub fn invoke<'a>(
        &'a self,
        target: &'a mut dyn ReflectCall,
        args: JsonValue,
    ) -> BoxFuture<'a, Result<JsonValue>> {
        (self.function)(target, args)
    }
}

/// 反射调用 trait
pub trait ReflectCall: Send + Sync {
    /// 动态调用方法
    fn call_method<'a>(
        &'a mut self,
        name: &'a str,
        args: JsonValue,
    ) -> BoxFuture<'a, Result<JsonValue>>;

    fn as_any(&self) -> &dyn Any;

    fn as_any_mut(&mut self) -> &mut dyn Any;
}
/// 用于动态调用的入口函数
pub fn call_method<'a, T>(
    target: &'a mut T,
    method_name: &'a str,
    args: JsonValue,
) -> BoxFuture<'a, Result<JsonValue>>
where
    T: ReflectCall,
{
    target.call_method(method_name, args)
}

#[derive(ReflectCall)]
struct Player {
    health: u32,
}

#[reflect_method]
impl Player {
    // 结构体参数的方法
    pub async fn get_sender_none(&self) -> String {
        let req = SenderItem {
            local_msg_id: 1,
            sender_name: "sender_name".to_string(),
        };
        println!(
            "get_sender_none: id: {}, name: {}",
            req.local_msg_id, req.sender_name
        );
        "sender_name".to_string()
    }
}

#[reflect_method]
impl Player {
    pub async fn get_sender_info(&self, req: GetSenderNameRequest, a: i32, b: String) -> Result<String> {
        if req.a == 100 {
            return Err(anyhow!("get sender info error"));
        }
        Ok(format!("req.a: {}, req.b: {}, a: {}, b: {}", req.a, req.b, a, b))
    }

    pub async fn set_stats(&self, health: u32, strength: u32) {
        println!("Set health: {}, strength: {}", health, strength);
    }

    pub async fn update_stats(&self, health: u32, strength: u32) {
        println!("Updating health: {}, strength: {}", health, strength);
    }
    pub async fn add_health(&mut self) {
        self.health += 1;
        println!("Health: {}", self.health);
    }
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SenderItem {
    pub local_msg_id: i64,
    pub sender_name: String,
}

#[derive(Serialize, Deserialize)]
pub struct GetSenderNameRequest {
    pub a: i32,
    pub b: String,
}

struct MethodArgs {
    args: JsonValue,
    method_name: String,
}

#[derive(Serialize, Deserialize)]
struct Status {
    code: i32,
    message: String,
}

#[derive(Serialize, Deserialize)]
struct Response {
    status: Status,
    data: Value,
}

impl Response {
    fn build(code: i32, message: String, data: Value) -> Self {
        Response {
            status: Status { code, message },
            data,
        }
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let mut player = Player { health: 100 };

    let req_args = serde_json::json!({
        "req": {
            "b": "example",
            "a": 100,
        },
        "b": "example",
        "a": 100,
    });

    let args = serde_json::json!({
        "strength": 50,
        "health": 120,
    });

    let methodargs = vec![
        MethodArgs {
            args: req_args,
            method_name: "get_sender_info".to_string(),
        },
        MethodArgs {
            args: serde_json::json!({}),
            method_name: "get_sender_none".to_string(),
        },
        MethodArgs {
            args: args,
            method_name: "update_stats".to_string(),
        },
        MethodArgs {
            args: serde_json::json!({}),
            method_name: "add_health".to_string(),
        },
    ];
    for methodarg in methodargs {
        match call_method(&mut player, &methodarg.method_name, methodarg.args).await {
            Ok(result) => {
                let response = Response::build(0, "".to_string(), result);
                println!("Result: {:?}", serde_json::to_string(&response));
            }
            Err(err) => {
                println!("{err}");
            }
        }

    }

    Ok(())
}
